/**
 * 运行时状态管理模块
 * 使用单例模式确保全局唯一实例
 * 通过代理模式确保状态只能通过指定方法修改
 *
 * 主要功能：
 * 1. 管理应用程序的运行时状态（用户信息、登录状态等）
 * 2. 通过 IPC 通信实现主进程和渲染进程之间的状态同步
 * 3. 管理运行时状态（如用户信息等）
 */

import { app, ipcMain, webContents } from 'electron'

import { topic } from '../common/config'

import { toRaw, watch, shallowRef, type Ref } from '@vue/reactivity'

/**
 * 运行时状态管理器类
 * 实现单例模式，确保全局只有一个状态管理实例
 */
class RuntimeStateManager {
  private static instance: RuntimeStateManager // 单例实例
  private state: Record<string, Ref<unknown>> = {} // 运行时状态
  private rendererIds = new Map<string, number[]>() // 已注册的渲染进程 webContentsId
  private mainRefs = new Map<string, number>() // 主进程引用计数


  private constructor() {
    app.whenReady().then(async () => {
      this.setupIpcHandlers()
    })
  }

  /**
   * 获取全局状态管理器的单例实例
   * @returns {runtimeStateManager} 全局状态管理器实例
   */
  public static getInstance(): RuntimeStateManager {
    if (!RuntimeStateManager.instance) {
      RuntimeStateManager.instance = new RuntimeStateManager()
    }
    return RuntimeStateManager.instance
  }

  /**
   * 更新运行时状态
   * @param key - 要更新的状态键
   * @param newValue - 新的状态值
   */
  public set(key: string, newValue: unknown): void {
    let oldValue = this.state[key]?.value
    if (newValue !== undefined) {
      if (!oldValue) {
        this.state[key] = shallowRef(newValue)
      } else {
        this.state[key].value = newValue
      }
    } else {
      delete this.state[key]
    }
    this.update(key, newValue, oldValue)
  }

  /**
   * 监听指定键的状态变化
   * @param key - 要监听的键名
   * @param callback - 状态变化回调函数
   * @returns Vue的watch停止函数
   */
  watch<T>(key: string, callback: (newValue: T, oldValue: T) => void) {
    return watch(this.state[key], callback)
  }

  update(key: string, newValue?: unknown, oldValue?: unknown) {
    // 只通知已注册的渲染进程
    const ids = this.rendererIds.get(key)
    if (ids) {
      ids.forEach(id => {
        const contents = webContents.fromId(id)
        if (contents && !contents.isDestroyed()) {
          contents.send(`${topic.updateRuntime}-${key}`, {
            oldValue: toRaw(oldValue),
            newValue: toRaw(newValue)
          })
        } else {
          ids.splice(ids.indexOf(id), 1)
        }
      })
    }
  }

  public create<T>(name: string, defaultValue?: T) {
    // 主进程获取/创建状态并增加引用计数
    return this.acquireMain<T>(name, defaultValue)
  }

  /**
   * 获取运行时状态的指定属性
   * @param key - 要获取的状态键
   * @param defaultValue - 默认值
   * @returns 指定属性的值
   */
  private get<T>(key: string, defaultValue?: T) {
    if (!this.state[key]) {
      this.state[key] = shallowRef(defaultValue)
    }
    return this.state[key].value as T
  }

  /**
   * 主进程获取/创建状态并增加引用计数
   */
  public acquireMain<T>(key: string, defaultValue?: T) {
    const current = this.get<T>(key, defaultValue)
    const count = this.mainRefs.get(key) ?? 0
    this.mainRefs.set(key, count + 1)
    return current
  }

  /**
   * 主进程释放引用；当无渲染进程引用且主进程引用为0时，真正销毁状态
   */
  public releaseMain(key: string) {
    const count = this.mainRefs.get(key) ?? 0
    if (count > 1) {
      this.mainRefs.set(key, count - 1)
    } else {
      this.mainRefs.delete(key)
      this.maybeCleanup(key)
    }
  }

  /**
   * 在引用都释放时清理状态
   */
  private maybeCleanup(key: string) {
    const mainCount = this.mainRefs.get(key) ?? 0
    const ids = this.rendererIds.get(key)
    if (mainCount === 0 && (!ids || ids.length === 0)) {
      // 双方都已销毁，真正清理状态
      this.set(key, undefined)
      if (ids) this.rendererIds.delete(key)
    }
  }

  /**
   * 设置 IPC 通信处理器
   * 处理来自渲染进程的状态获取和更新请求
   */
  private setupIpcHandlers(): void {
    // 运行时状态处理器
    ipcMain.on(topic.setRuntime, (_, data: { key: string; value: unknown }) => {
      this.set(data.key, data.value)
    })

    ipcMain.on(topic.getRuntime, (event, key: string) => {
      let ids = this.rendererIds.get(key)
      if (!ids) {
        ids = [event.sender.id]
        this.rendererIds.set(key, ids)
      } else if (!ids.includes(event.sender.id)) {
        ids.push(event.sender.id)
      }
      event.returnValue = toRaw(this.get(key))
    })

    ipcMain.on(topic.clearRuntime, (event, key: string) => {
      const ids = this.rendererIds.get(key)
      if (ids) {
        const index = ids.indexOf(event.sender.id)
        if (index !== -1) {
          ids.splice(index, 1)
        }
        if (ids.length === 0) {
          this.rendererIds.delete(key)
          // 仅当主进程也无引用时才真正销毁
          this.maybeCleanup(key)
        }
      }
    })
  }
}

// 导出单例实例
export const runtimeStateManager = RuntimeStateManager.getInstance()
